#include <iostream>
#include <functional>
#include <unordered_map>
#include "unistd.h"

#include "muduo/proto/codec.h"
#include "muduo/proto/dispatcher.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

#include "../mqcommom/threadpool.hpp"
#include "virtualhost.hpp"
#include "connection.hpp"
#include "consumer.hpp"

#include "../mqcommom/msg.pb.h"
#include "../mqcommom/proto.pb.h"
using namespace std;

namespace ns_rabbitmq
{
#define DBFILE "/meta.db"
#define HOSTNAME "MyVirtualHost"
    class Server
    {
    public:
        typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
        Server(int port, const std::string &basedir) : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),
                                                               "Server", muduo::net::TcpServer::kReusePort),
                                                       _dispatcher(std::bind(&Server::onUnknownMessage, this, std::placeholders::_1,
                                                                             std::placeholders::_2, std::placeholders::_3)),
                                                       _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher,
                                                                                                        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),
                                                       _host(std::make_shared<VirtualHost>(HOSTNAME, basedir, basedir + DBFILE)),
                                                       _consumers(std::make_shared<ConsumerMananger>()),
                                                       _connections(std::make_shared<ConnectionManager>()),
                                                       _threadpool(std::make_shared<ThreadPool>())

        {
            // 恢复的队列，用来初始化消费者
            MsgQueMap qmap = _host->AllQueues();
            for (auto &queue : qmap)
            {
                _consumers->InitQueueConsumer(queue.first);
            }
            // 为派发器注册方法
            _dispatcher.registerMessageCallback<OpenChannelRequest>(std::bind(&Server::OnOpenChannel, this, std::placeholders::_1,
                                                                              std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<CloseChannelRequest>(std::bind(&Server::OnCloseChannel, this, std::placeholders::_1,
                                                                               std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<DeclearExchangeRequest>(std::bind(&Server::OnDeclearExchange, this, std::placeholders::_1,
                                                                                  std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<DeleteExchangeRequest>(std::bind(&Server::OnDeleteExchange, this, std::placeholders::_1,
                                                                                 std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<DeclearQueueRequest>(std::bind(&Server::OnDeclearQueue, this, std::placeholders::_1,
                                                                               std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<DeleteQueueRequest>(std::bind(&Server::OnDeleteQueue, this, std::placeholders::_1,
                                                                              std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<QueueBindRequest>(std::bind(&Server::OnQueueBind, this, std::placeholders::_1,
                                                                            std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<QueueUnbindRequest>(std::bind(&Server::OnQueueUnbind, this, std::placeholders::_1,
                                                                              std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<BasicPublishRequest>(std::bind(&Server::OnBasicPublish, this, std::placeholders::_1,
                                                                               std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<BasicConsumerRequest>(std::bind(&Server::OnBasicConsumer, this, std::placeholders::_1,
                                                                                std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<BasicAckRequest>(std::bind(&Server::OnBasicAck, this, std::placeholders::_1,
                                                                           std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<BasicCancelRequest>(std::bind(&Server::OnBasicCancel, this, std::placeholders::_1,
                                                                              std::placeholders::_2, std::placeholders::_3));
            // 为服务器绑定方法
            _server.setConnectionCallback(std::bind(&Server::OnConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1,
                                                 std::placeholders::_2, std::placeholders::_3));
        }
        void Start()
        {
            _server.start();
            _baseloop.loop();
        }

    private:
        void onUnknownMessage(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp)
        {
            std::cout << "onUnknownMessage: " << message->GetTypeName();
            conn->shutdown();
        }
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                _connections->NewConnection(conn, _codec, _consumers, _host, _threadpool);
                cout << "建立连接" << endl;
            }

            else
            {
                _connections->DeleteConnection(conn);
                cout << "断开连接";
            }
        }

        // 业务的回调
        void OnOpenChannel(const muduo::net::TcpConnectionPtr &conn, const OpenChannelRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("打开信道失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            mconn->OpenChannel(message);
        }
        void OnCloseChannel(const muduo::net::TcpConnectionPtr &conn, const CloseChannelRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("删除信道失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            mconn->CloseChannel(message);
        }

        void OnDeclearExchange(const muduo::net::TcpConnectionPtr &conn, const DeclearExchangeRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("声明交换机失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("声明交换机失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->DeclearExchange(message);
        }
        void OnDeleteExchange(const muduo::net::TcpConnectionPtr &conn, const DeleteExchangeRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("删除交换机失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("删除交换机失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->DeleteExchange(message);
        }
        void OnDeclearQueue(const muduo::net::TcpConnectionPtr &conn, const DeclearQueueRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("声明队列失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("声明队列失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->DeclareQueue(message);
        }
        void OnDeleteQueue(const muduo::net::TcpConnectionPtr &conn, const DeleteQueueRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("删除队列失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("删除队列失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->DeleteQueue(message);
        }

        void OnQueueBind(const muduo::net::TcpConnectionPtr &conn, const QueueBindRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("绑定队列失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("绑定队列失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->QueueBind(message);
        }
        void OnQueueUnbind(const muduo::net::TcpConnectionPtr &conn, const QueueUnbindRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("解除绑定队列失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("解除绑定队列失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->QueueUnbind(message);
        }

        void OnBasicPublish(const muduo::net::TcpConnectionPtr &conn, const BasicPublishRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("消息推送失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("消息推送失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            BasicProperties bp = message->propertie();
            channel->BasicPublish(message);
        }
        void OnBasicConsumer(const muduo::net::TcpConnectionPtr &conn, const BasicConsumerRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("消费失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("消费失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->BasicConsume(message);
        }
        void OnBasicAck(const muduo::net::TcpConnectionPtr &conn, const BasicAckRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("ack失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("ack失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->BasicAck(message);
        }
        void OnBasicCancel(const muduo::net::TcpConnectionPtr &conn, const BasicCancelRequestptr &message, muduo::Timestamp t)
        {
            ConnectionPtr mconn = _connections->GetConnPtr(conn);
            if (!mconn.get())
            {
                ELOG("删除消息失败，没有找到连接对象!");
                conn->shutdown();
                return;
            }
            ChannelPtr channel = mconn->GetChannel(message->cid());
            if (!channel.get())
            {
                ELOG("删除消息失败，没有找到信道对象!");
                conn->shutdown();
                return;
            }
            channel->BasicCancel(message);
        }
        // 业务的回调

    private:
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        ProtobufDispatcher _dispatcher;
        ProtobufCodecPtr _codec;
        HostPtr _host;
        ConsumerManangerPtr _consumers;
        ConnectionManagerPtr _connections;
        ThreadPoolPtr _threadpool;
    };
}